<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>class,装饰模式,代理模式(proxy)</title>
</head>

<body>
  <script>
    /**
     *  class 类
     *  js 没有所谓类个感念 
     *     所谓的class只是一个语法糖
     *     为了让开发者更清晰的面向对象编程
     * 
     */
    class people {
      // constructor 类似于es5构建一个对象的init函数
      constructor(type) { //type为构造时传入的值
        console.log(type)
        this.type = type
      }
      spake = () => {
        console.log("spake");
      };
      study = () => {
        console.log("study");
      };
    }
    var boy = new people("boy"); //此时 constructor 函数的 type为boy;
    var girl = new people("girl");
    //此时的构造出 boy 与 girl, type 不同，且都存在 spake 和 study 方法;
    console.log(boy, girl); //构建完成后 boy 与 girl 已成为独立的对象
    // 可以各种扩展
    boy.run = () => {
      console.log('run')
    }
    girl.jump = () => {
      console.log('jump')
    }
    /**
     * 小结
     *  
     * class 基本与普通构造函数功能相同，实现对象的继承，有多态的效果。
     * 
     * 
     * 不同点在于： 
     *    1 用 class后 new 出的实例会直接带有继承的方法以及属性， 
     *      而不是存在于 隐式原型链 __proto__ 中
     *      构造函数people的prototype中也不存在实例所继承的方法属性 
     * 
     *    2 但构造函数的prototype仍然会与生成实例的__proto__指向同一个堆内存。
     *      原型链依然存在；
     */

    /**
     *  装饰者模式的思想
     *  装饰着模式为一种开发思想，
     *  意在将装饰的内容统统放置到一个类中方便管理，
     *  类似于房子装修，
     *  我们将所有的装修风格放置一个类中，
     *  将成品对象（房子）传入，
     *  导出装饰成功的房子
     */

    //首先创建一个装饰类
    class Decorate { //装饰类
      get = (obj, key) => { //get方法为该类对象的装饰方法（类似于装修工）
        obj[key] = this[key];
      };
      read = () => {
        console.log("read");
      };
      swim = () => {
        console.log("swim");
      };
    };

    var decorate = new Decorate(); //获取一个装饰类
    // 将 class 中得到的 boy 与 girl 分别学习read与swim
    decorate.get(boy, "read"); // boy学习read
    decorate.get(girl, "swim"); // girl学习swim
    console.log(boy, girl); //实现

    /**
     *  小结
     * 
     *  构造使我们的对象获取到更多的公共方法，
     *  装饰则可以让我们对症下药，
     *  使不同的对象获取新的不同的功能。
     *  合理的使用可以使我们的代码统一化和规范化，
     *  方便日后的维护扩展和更新。
     * 
     */


    /**
     *  代理模式和新API=>Proxy
     *  代理模式和Proxy 类似于拦截器的功能，
     *  当我们读取或者操作挂载的该对象时，
     *  可以按照我们的意愿去执行一些方法
     */
    var message = { //有一个message对象，并包含boy和girl,
      boy,
      girl,
    };

    //利用Proxy监听该对象
    var obj = new Proxy(message, { //代理模式，拦截器的使用代替访问器属性
      set(target, prop, val, date) { //set函数当 obj对象被修改时会触发
        //target要修改的对象，prop修改的值，val修改后的值,date生成的obj实例
        console.log(target, prop, val, date);
        console.log(target === message); //true;
        console.log(date === obj); //true;
        //如果省略此句则不会发生改变 此处也可以改变赋值后的内容
        target[prop] = val;
      },
      get(target, property, date) { //get函数当 obj对象某个属性被访问时会触发
        //target访问的对象 property访问属性的key值 val生成的obj实例
        console.log(target, property, date);
        console.log(target === message); //true;
        console.log(date === obj); //true;
        return target[property]; //return的值为我们打印读取的值
      }
    });

    //会返回一个新的实例 obj   注：console.log(obj === message)为false;
    obj.c = 2; //操作 obj 会触发set 
    message.d = 3; //操作 message 则不会触发
    // 但两者都会在 obj 和 message 对象中同时添加 c 或者 d 属性；
    console.log(obj.d, message.c) // 3,2


    // 附：传统的对象监听，访问器属性
    var box = {
      x:'x',
      y:1
    }
    var boxY = ''
    Object.defineProperty(box,'y',{ //box监听的对象  key访问属性的key值
      configurable: true,
      enumerable: true,
      set: function (value) { //操作obj触发
        boxY = value
      },
      get: function () { //访问obj某个key值
        return boxY; //同样需要return
      }
    });
    box.y = 3
    console.log(box,boxY)
  </script>
</body>

</html>